<?php

namespace Hx;

/**
 * Class App
 * @package Hx
 */
class App {

    /**
     * 当前App对象
     * @var App
     */
    private static $app;

    /**
     * 设置App参数
     * @param string $name
     * @param string $path
     */
    public static function create($name, $path) {
        if (self::$app) {
            if (self::$app->getName() === $name) {
                return;
            }
        }
        self::$app = new App($name, $path);
    }

    /**
     * 获取App对象
     * @return App
     */
    public static function getInstance() {
        return self::$app;
    }

    /**
     * 运行App
     */
    public static function run() {
        if (self::$app) {
            self::$app->dispatch()
                ->output();
            return;
        }
        die('no application instance');
    }

    /**
     * 抛出错误
     * @param string $message
     * @param int $statusCode
     */
    public static function error($message, $statusCode = 500) {
        /**
         * @var \Hx\Http\Response $response
         */
        $response = self::$app->getClass('Hx\Http\Response');
        $response->setStatus($statusCode);
        $response->setBody($message);
        $response->send();
        exit;
    }

    //---------------------

    /**
     * 应用名称
     * 和命名空间一致
     * @var string
     */
    private $name;

    /**
     * 应用目录
     * @var string
     */
    private $path;

    /**
     * App构造方法
     * @param string $name
     * @param string $path
     */
    private function __construct($name, $path) {
        $this->name = $name;
        $this->path = $path;
        #执行预备工作
        $this->prepare();
    }

    /**
     * 获取App名称
     * @return string
     */
    public function getName() {
        return $this->name;
    }

    /**
     * 获取App目录
     * @return string
     */
    public function getPath() {
        return $this->path;
    }

    //-------------------------------------

    /**
     * 记录加载的类库
     * @var array
     */
    private $imported;

    /**
     * 加载类库
     * @param string $className
     * @param bool $return 是否返回bool，否则抛出错误
     * @return bool
     */
    public function import($className, $return = FALSE) {
        #判断是否已经引入
        if ($this->imported[$className]) {
            return TRUE;
        }
        $file = '';
        if (strpos($className, __NAMESPACE__) === 0) { #处理Hx命名空间下的核心库类
            $file = str_replace(array('\\', __NAMESPACE__ . '/'), array('/', __DIR__ . '/'), $className) . '.php';
        } elseif (strpos($className, $this->getName()) === 0) { #处理应用命名空间下的库类
            $file = str_replace(array('\\', $this->getName() . '/'), array('/', $this->getPath()), $className) . '.php';
        }
        if (is_file($file)) {
            require_once $file;
            $this->imported[$className] = $file;
            return TRUE;
        }
        if ($return) {
            return FALSE;
        }
        self::error('no class : ' . $className);
        return FALSE;
    }

    /**
     * 预备工作
     * @return $this
     */
    private function prepare() {
        #加载配置
        $this->loadConfig();
        #注册自动加载函数
        spl_autoload_register(array($this, 'import'));
        #注册异常处理函数
        set_exception_handler(function (\Exception $exc) {
            self::error($exc->getMessage(), 500);
        });
        return $this;
    }

    //--------------------------------------

    /**
     * 路由类
     * @var Router
     */
    private $router;

    /**
     * 路由分发结果
     * @var array|bool
     */
    private $mapResult;

    /**
     * 路由执行结果
     * @var mixed
     */
    private $appResult;

    /**
     * 路由分发
     * @return $this
     */
    private function dispatch() {
        #注册路由类
        $this->router = $this->getClass('Hx\Router', 'router', TRUE);
        $_url = parse_url($_GET['_url'], PHP_URL_PATH);
        #分发路由
        $this->mapResult = $this->router->map($_url);
        if ($this->mapResult === FALSE) {
            self::error('no route match : ' . $_url, 404);
        }
        #执行路由
        $controllerName = $this->name . '\Controller\\' . $this->mapResult['callback'][0];
        $controller = new $controllerName();
        $method = $this->mapResult['callback'][1] . 'Action';
        if (!is_callable(array($controller, $method))) {
            self::error('no executable controller : ' . $controllerName . '->' . $method);
        }
        $this->appResult = call_user_func_array(array($controller, $method), $this->mapResult['args']);
        return $this;
    }

    /**
     * 获取App的路由类
     * @return Router
     */
    public function getRouter() {
        return $this->router;
    }

    //---------------------------------------------

    /**
     * 输出结果
     * @return int
     */
    public function output() {
        if ($this->appResult instanceof \Hx\Mvc\View\Base) {
            $this->appResult->display();
            return 1;
        }
        if ($this->appResult instanceof \Hx\Http\Response) {
            $this->appResult->send();
            return 1;
        }
        if (is_string($this->appResult)) {
            /**
             * @var \Hx\Http\Response $response
             */
            $response = $this->getClass('\Hx\Http\Response');
            $response->setBody($this->appResult);
            $response->send();
            return 1;
        }
        return 1;
    }

    //------------------------

    /**
     * 加载配置
     */
    private function loadConfig() {
        $file = $this->path . 'config.inc';
        if (!is_file($file)) {
            exit('no config file');
        }
        $this->config = require($file);
        #处理FILE:标识的文件配置数据
        foreach ($this->config as $k => $v) {
            if (is_string($v) && stripos($v, 'FILE:') === 0) {
                $file = $this->path . 'Config/' . ltrim($v, 'FILE:');
                $this->config[$k] = is_file($file) ? require($file) : NULL;
            }
        }
    }

    /**
     * 配置数据
     * @var array
     */
    private $config = array();

    /**
     * 获取配置
     * @param string $key
     * @return mixed
     */
    public function getConfig($key) {
        if ($key === TRUE) {
            return $this->config;
        }
        $key = '["' . str_replace('.', '"]["', $key) . '"]';
        return eval('return $this->config' . $key . ';');
    }

    //-------------------------

    /**
     * 实例化类对象
     * @var array
     */
    private $class = array();

    /**
     * 获取实例对象
     * @param string $className 类名称
     * @param null|string $arg 类标识名称
     * @param bool $useConfig 调用配置数据，配置标识和类标识一致
     * @return mixed
     */
    public function getClass($className, $arg = NULL, $useConfig = FALSE) {
        $keyName = $className . (!$arg ? '' : ':' . $arg);
        if (!$this->class[$keyName]) {
            if ($useConfig && $arg) {
                $arg = $this->getConfig($arg);
            }
            $class = new $className($arg);
            $this->class[$keyName] = $class;
        }
        return $this->class[$keyName];
    }
}